package com.yitong.spiboot;

import org.apache.catalina.Wrapper;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.ApplicationFilterRegistration;
import org.apache.catalina.core.ApplicationServletRegistration;
import org.apache.catalina.core.StandardContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.ui.context.ThemeSource;
import org.springframework.ui.context.support.UiApplicationContextUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.ServletContextScope;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.*;
import java.lang.reflect.Field;
import java.util.Collection;

public class MyServletWebServerApplicationContext extends AnnotationConfigServletWebServerApplicationContext {
    static Integer INIT = 1;

    private static final Log logger = LogFactory.getLog( ServletWebServerApplicationContext.class );
    private ThemeSource themeSource;
    ServletContext servletContext;
    org.springframework.context.ApplicationContext applicationContext;
    private String pid;


    public MyServletWebServerApplicationContext(ServletContext bean1) {
        super();
        this.INIT = 1;
        this.servletContext=bean1;
    }


    @Override
    protected void onRefresh() {
        themeSource = UiApplicationContextUtils.initThemeSource( this );
        try {
            createWebServer();
        } catch (Throwable ex) {
            ex.printStackTrace();
            throw new ApplicationContextException( "Unable to start web server", ex );
        }
        initPropertySources();
    }

    private void createWebServer() throws ServletException {

        getSelfInitializer().onStartup( servletContext );
        initPropertySources();
//        this.start();
    }


    private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
        return this::selfInitialize;
    }

    private void selfInitialize(ServletContext servletContext) throws ServletException {
        logger.info( "初始化{" + INIT + "}" );
        prepareWebApplicationContext( servletContext );
        registerApplicationScope( servletContext );

        WebApplicationContextUtils.registerEnvironmentBeans( getBeanFactory(), servletContext );

        Collection <ServletContextInitializer> servletContextInitializerBeans = getServletContextInitializerBeans();

        for (ServletContextInitializer beans : servletContextInitializerBeans) {
            if (beans instanceof DispatcherServletRegistrationBean) {
                DispatcherServletRegistrationBean beans1 = (DispatcherServletRegistrationBean) beans;
                ServletRegistration.Dynamic dynamic = addRegistration( beans1, servletContext );
                dynamic.setAsyncSupported( true );
                continue;
            }
            if(beans instanceof FilterRegistrationBean){
                FilterRegistrationBean beans1 = (FilterRegistrationBean) beans;
                FilterRegistration.Dynamic dynamic = addFilter( beans1, servletContext );
                continue;
            }
            if(beans instanceof DelegatingFilterProxyRegistrationBean){
                DelegatingFilterProxyRegistrationBean beans1 = (DelegatingFilterProxyRegistrationBean) beans;
                FilterRegistration.Dynamic dynamic = addFilter( beans1, servletContext );
                continue;
            }

            else {
                beans.onStartup( servletContext );
            }
        }


        INIT++;
    }

    private void registerApplicationScope(ServletContext servletContext) {
        ServletContextScope appScope = new ServletContextScope( servletContext );
//        getBeanFactory().registerScope( WebApplicationContext.SCOPE_APPLICATION, appScope );
        // Register as ServletContext attribute, for ContextCleanupListener to detect it.
        servletContext.setAttribute( ServletContextScope.class.getName(), appScope );
    }



    protected void prepareWebApplicationContext(ServletContext servletContext) {
        Object rootContext = servletContext.getAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE );

        servletContext.log( "Initializing Spring embedded WebApplicationContext" );
        try {
            setServletContext( servletContext );
            if (logger.isInfoEnabled()) {
                long elapsedTime = System.currentTimeMillis() - getStartupDate();
                logger.info( "Root WebApplicationContext: initialization completed in " + elapsedTime + " ms" );
            }
        } catch (RuntimeException | Error ex) {
            logger.error( "Context initialization failed", ex );
            servletContext.setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex );
            throw ex;
        }
    }

    protected ServletRegistration.Dynamic addRegistration(DispatcherServletRegistrationBean bean, ServletContext servletContext) {
        try {
            bean.setName( bean.toString() );
            String description = bean.getServletName();
            DispatcherServlet dispatcherServlet = bean.getServlet();
            Field fieldApplicationContext = servletContext.getClass().getDeclaredField( "context" );//利用反射获取ApplicationContext对象
            fieldApplicationContext.setAccessible( true );//使私有可获取
            ApplicationContext applicationContext = (ApplicationContext) fieldApplicationContext.get( servletContext );//获取到ApplicationContext对象

            Field fieldStandardContext = applicationContext.getClass().getDeclaredField( "context" );//利用反射获取StandardContext对象
            fieldStandardContext.setAccessible( true );//使私有可获取
            StandardContext context = (StandardContext) fieldStandardContext.get( applicationContext );//获取到StandardContext对象
            Wrapper wrapper = (Wrapper) context.findChild( description );
            if (wrapper == null) {
                wrapper = context.createWrapper();
                wrapper.setName( description );
                context.addChild( wrapper );
            } else {
                throw new RuntimeException("添加上下文已存在请更换上下文"+wrapper.getName());
            }

            ServletRegistration.Dynamic app = new ApplicationServletRegistration( wrapper, context );
            wrapper.setServletClass( dispatcherServlet.getClass().getName() );
            wrapper.setServlet( dispatcherServlet );
            app.setLoadOnStartup( 0 );
            app.addMapping( bean.getServletUrlMapping() );
            return app;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

    }
    private FilterRegistration.Dynamic addFilter(FilterRegistrationBean bean, ServletContext servletContext)  {

        try {
            String filterName = bean.toString();
            bean.setName( filterName );
            Filter filter = bean.getFilter();
            Field fieldApplicationContext = servletContext.getClass().getDeclaredField( "context" );//利用反射获取ApplicationContext对象
            fieldApplicationContext.setAccessible( true );//使私有可获取
            ApplicationContext applicationContext = (ApplicationContext) fieldApplicationContext.get( servletContext );//获取到ApplicationContext对象

            Field fieldStandardContext = applicationContext.getClass().getDeclaredField( "context" );//利用反射获取StandardContext对象
            fieldStandardContext.setAccessible( true );//使私有可获取
            StandardContext context = (StandardContext) fieldStandardContext.get( applicationContext );

            FilterDef filterDef = context.findFilterDef(filterName);

            // Assume a 'complete' FilterRegistration is one that has a class and
            // a name
            if (filterDef == null) {
                filterDef = new FilterDef();
                filterDef.setFilterName(filterName);
                context.addFilterDef(filterDef);
            } else {
                if (filterDef.getFilterName() != null &&
                        filterDef.getFilterClass() != null) {
                    return null;
                }
            }

            filterDef.setFilterClass(filter.toString());
            filterDef.setFilter(filter);

            return new ApplicationFilterRegistration(filterDef, context);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
    private FilterRegistration.Dynamic addFilter(DelegatingFilterProxyRegistrationBean bean, ServletContext servletContext)  {

        try {
            String filterName = bean.toString();
            bean.setName( filterName );
            Filter filter = bean.getFilter();
            Field fieldApplicationContext = servletContext.getClass().getDeclaredField( "context" );//利用反射获取ApplicationContext对象
            fieldApplicationContext.setAccessible( true );//使私有可获取
            ApplicationContext applicationContext = (ApplicationContext) fieldApplicationContext.get( servletContext );//获取到ApplicationContext对象

            Field fieldStandardContext = applicationContext.getClass().getDeclaredField( "context" );//利用反射获取StandardContext对象
            fieldStandardContext.setAccessible( true );//使私有可获取
            StandardContext context = (StandardContext) fieldStandardContext.get( applicationContext );

            FilterDef filterDef = context.findFilterDef(filterName);

            // Assume a 'complete' FilterRegistration is one that has a class and
            // a name
            if (filterDef == null) {
                filterDef = new FilterDef();
                filterDef.setFilterName(filterName);
                context.addFilterDef(filterDef);
            } else {
                if (filterDef.getFilterName() != null &&
                        filterDef.getFilterClass() != null) {
                    return null;
                }
            }

            filterDef.setFilterClass(filter.toString());
            filterDef.setFilter(filter);

            return new ApplicationFilterRegistration(filterDef, context);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void setApplicationContext(String pid,org.springframework.context.ApplicationContext applicationContext) {
        this.pid=pid;
        this.applicationContext=applicationContext;
        if(applicationContext.isSingleton( "dataSource" )){

            this.getBeanFactory().registerSingleton( "dataSource",applicationContext.getBean( "dataSource" ) );
        }
    }



    @Override
    public void start() {
        super.start();
        System.out.println("启动了");
    }
}
